//
//  RACSubscriber+AFProgressCallbacks.m
//  Reactive AFNetworking Example
//
//  Created by Robert Widmann on 3/28/13.
//  Copyright (c) 2013 CodaFi. All rights reserved.
//

#import "RACSubscriber+ADProgress.h"
#import "RACPassthroughSubscriber.h"  
#import <objc/runtime.h>

@interface RACPassthroughSubscriber (ADProgress)

- (void)sendProgress:(float)value;

@end

@implementation RACPassthroughSubscriber(ADProgress)

- (void)sendProgress:(float)value {
    RACDisposable *disposable = [self performSelector:@selector(disposable)];
    if (disposable.disposed) return;
    
    id<RACSubscriber> innerSubscriber = [self valueForKey:@"innerSubscriber"];
    if([innerSubscriber isKindOfClass:[RACPassthroughSubscriber class]]){
        [(RACPassthroughSubscriber*)innerSubscriber sendProgress:value];
    }else if([innerSubscriber isKindOfClass:[RACSubscriber class]]){
        [(RACSubscriber*)innerSubscriber sendProgress:value];
    }else if([innerSubscriber isKindOfClass:[RACSubject class]]){
        [(RACSubject*)innerSubscriber sendProgress:value];
    }else{
        NSAssert(0, @"Not Recognized Object");
    }
}

@end

static char ADProgress_Block_Key;

@implementation RACSubscriber (ADProgress)

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+ (instancetype)subscriberWithNext:(void (^)(id x))next progress:(void (^)(float progress))progress error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
	RACSubscriber *subscriber = [self subscriberWithNext:next error:error completed:completed];
	subscriber.progress = progress;
	return subscriber;
}
#pragma clang diagnostic pop

- (void)setProgress:(void (^)(float))progress {
    objc_setAssociatedObject(self, &ADProgress_Block_Key, progress, OBJC_ASSOCIATION_COPY);
}

- (void (^)(float))progress {
    return objc_getAssociatedObject(self, &ADProgress_Block_Key);
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (void)sendProgress:(float)value {
    RACDisposable *disposable = [self performSelector:@selector(disposable)];
    if (disposable.disposed) return;
    
    if (self.progress != NULL) self.progress(value);
}
#pragma clang diagnostic pop

@end

@implementation RACSignal (ADProgress)

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progressBlock next:(void (^)(id x))nextBlock {
    NSParameterAssert(progressBlock != NULL);
    NSParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock progress:progressBlock error:NULL completed:NULL];
    
    return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progressBlock next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock {
	NSParameterAssert(progressBlock != NULL);
	NSParameterAssert(nextBlock != NULL);
	NSParameterAssert(errorBlock != NULL);

	RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock progress:progressBlock error:errorBlock completed:NULL];
	
	return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progressBlock next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
    NSParameterAssert(progressBlock != NULL);
    NSParameterAssert(nextBlock != NULL);
    NSParameterAssert(errorBlock != NULL);
    NSParameterAssert(completedBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock progress:progressBlock error:errorBlock completed:completedBlock];
    
    return [self subscribe:o];
}

@end

@implementation RACSubject (ADProgress)

- (void)sendProgress:(float)value {
	void (^subscriberBlock)(id<RACSubscriber> subscriber) = ^(id<RACSubscriber> subscriber){
		[(RACSubscriber*)subscriber sendProgress:value];
	};
    SEL performBlockSel = sel_registerName("enumerateSubscribersUsingBlock:");
    if([self respondsToSelector:performBlockSel]){
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:performBlockSel withObject:subscriberBlock];
#pragma clang diagnostic pop
    }else{
        NSAssert(0, @"not found enumerateSubscribersUsingBlock:");
    }
}

@end
